package com.xuanyue.componentim.datapersistence;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
public class MessagePersistenceHandler {

    @Resource
    private MessageCacheChannel messageCacheChannel;

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Resource//事务管理器
    private DataSourceTransactionManager transactionManager;



    private Base64.Decoder decoder = Base64.getDecoder();

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final String SAVE_SQL = "INSERT INTO message VALUES ( id, send_user_id, retrieve_user_id, message, state, send_time ) \r\n";

    private static final String READ_SQL = "UPDATE message SET state = 2 WHERE  \r\n";

    private static final String RECALL_SQL = "UPDATE message SET state = 3 WHERE id IN ";


    /**
     * 持久化
     */
    public void persistence(){

        BatchOrder batchOrder = messageCacheChannel.pullOrder();

        List<String> orders = batchOrder.getOrders();

        List<String> saveOrders = new LinkedList<>();
        List<String> readOrders = new LinkedList<>();
        List<String> recallOrders = new LinkedList<>();


        /* 指令分批-SQL */
        for (String order : orders) {

            /* 处理消息入库 */
            if( order.indexOf( "save:" ) == 0 )
                saveOrders.add( order );
            /* 处理已读标记 */
            if( order.indexOf( "read:" ) == 0 )
                readOrders.add( order );
            /* 处理消息撤回 */
            if( order.indexOf( "recall:" ) == 0 )
                recallOrders.add( order );
        }

        /* 保存消息sql拼接 */
        String saveSql = null;
        if( saveOrders.size() > 0 ){

            StringBuffer sb = new StringBuffer(SAVE_SQL);

            for (String saveOrder : saveOrders) {
                sb.append( orderToSqlSave( saveOrder ) ).append(",");
            }
            sb.setLength( sb.length() -1 );
            sb.append(";\r\n\r\n");

            saveSql = sb.toString();
        }

        /* 已读消息拼接-SQL*/
        String readSql = null;
        if( readOrders.size() > 0 ){

            Set<String> readSet = new HashSet<>();

            for (String readOrder : readOrders)
                readSet.add( orderToSqlRead( readOrder ) );

            StringBuffer sb = new StringBuffer( READ_SQL );
            Integer index = 0;
            for (String read : readSet) {

                String[] array = read.split(",");
                sb.append( "(send_user_id = \"" ).append(array[0]).append("\", retrieve_user_id = \"").append(array[1]).append("\")");
                if( index != readSet.size() -1 ){
                    sb.append( " OR \r\n" );
                }

                index++;
            }
            sb.append(";\r\n\r\n");
            readSql = sb.toString();
        }

        /* 消息撤回-SQL */
        String recallSql = null;
        if( recallOrders.size() > 0 ){

            StringBuffer sb = new StringBuffer( RECALL_SQL );
            sb.append("(");

            for (String recallOrder : recallOrders) {
                sb.append("\"").append( orderToSqlRecall( recallOrder ) ).append("\",");
            }
            sb.setLength( sb.length() - 1 );

            sb.append(");\r\n\r\n");

            recallSql = sb.toString();
        }
        /* 总装-SQL */
        StringBuffer sql = new StringBuffer();
        if( saveSql != null )
            sql.append( saveSql );

        if( readSql != null )
            sql.append( readSql );

        if( recallSql != null )
            sql.append( recallSql );

        /* 执行SQL */
        if( sql.length() > 0 ){
            /**
             * 为了保证临时缓存和数据库一致性
             * 则都成功或者都失败
             */
            TransactionDefinition def = new DefaultTransactionDefinition();
            TransactionStatus status = transactionManager.getTransaction(def);
            try {
                jdbcTemplate.execute( sql.toString() );

                /* 向通道发出处理成功信息,等待返回值,如果通道清除缓存失败则入库回滚 */
                if( messageCacheChannel.commitBatch( batchOrder.getBatchId() ) ){
                    transactionManager.commit(status);
                }
                else{
                    transactionManager.rollback(status);
                }

            }catch ( Exception e ){
                e.printStackTrace();
                /* 当提交数据库失败,向通道发出回滚批次,根据情况处理,比如redis需要释放锁 */
                transactionManager.rollback(status);
                messageCacheChannel.rollbackBatch( batchOrder.getBatchId() );
            }

        }
    }


    /**
     * 保存消息
     * @param order
     * @return
     */
    private String orderToSqlSave( String order ){

        String[] strArray = order.split(":");
        strArray = strArray[1].split(",");

        String id = strArray[0].substring(2);
        String s  = strArray[1].substring(2);
        String r  = strArray[2].substring(2);
        String mc = new String(decoder.decode(strArray[3].substring(1)), StandardCharsets.UTF_8);
        Date t  = new Date(Long.valueOf( strArray[4].substring(1) ));

        StringBuffer sb = new StringBuffer("(");
        sb.append( "\"" ).append( id ).append("\", ");
        sb.append( "\"" ).append( s  ).append("\", ");
        sb.append( "\"" ).append( r  ).append("\", ");
        sb.append( "\"" ).append( mc ).append("\", ");
        sb.append( "3, " );
        sb.append( "\"" ).append( sdf.format( t ) ).append("\") ");

        return sb.toString();
    }

    /**
     * 标记已读
     * @param order
     * @return
     */
    private String orderToSqlRead( String order ){

        String[] strArray = order.split(":");
        strArray = strArray[1].split(",");

        String s = strArray[0].substring(2);
        String r = strArray[1].substring(2);

        StringBuffer sb = new StringBuffer();
        sb.append( s ).append(",").append(r);

        return sb.toString();
    }

    /**
     * 消息撤回
     * @param order
     * @return
     */
    private String orderToSqlRecall( String order ){
        return order.substring( 9 );
    }

    public static void main(String[] args) {
//        String a = "save:s-sendUser,r-retrieveUserId,mc-bWVzc2FnZQ==,t-1665130708083;";
//        String b = "read:s-sendUser,r-retrieveUserId;";
//        String c = "recall:mi-121212;";
//        System.out.println( a.indexOf( "save:" ) );
//        System.out.println( b.indexOf( "read:" ) );
//        System.out.println( c.indexOf( "recall:" ) );

        System.out.println( "s-sendUser".substring( 1 ) );



    }

}
